package com.ansion.commonchat.startup.workmanager.bwork.impl;

import android.content.Context;

import androidx.annotation.NonNull;

import com.ansion.commonchat.startup.workmanager.bwork.BOneTimeWorkRequest;
import com.ansion.commonchat.startup.workmanager.bwork.BOperation;
import com.ansion.commonchat.startup.workmanager.bwork.BWorkContinuation;
import com.ansion.commonchat.startup.workmanager.bwork.BWorkInfo;
import com.ansion.commonchat.startup.workmanager.bwork.BWorkManager;
import com.ansion.commonchat.startup.workmanager.bwork.BWorkRequest;
import com.ansion.commonchat.startup.workmanager.bwork.BWorkerFactory;
import com.ansion.commonchat.startup.workmanager.bwork.impl.background.greedy.BGreedyScheduler;
import com.ansion.commonchat.startup.workmanager.bwork.impl.mode.WorkDataCache;
import com.ansion.commonchat.startup.workmanager.bwork.utils.BStartWorkRunnable;
import com.ansion.commonchat.startup.workmanager.bwork.utils.BStatusRunnable;
import com.ansion.commonchat.startup.workmanager.bwork.utils.BStopWorkRunnable;
import com.ansion.commonchat.startup.workmanager.bwork.utils.taskexecutor.BTaskExecutor;
import com.ansion.commonchat.startup.workmanager.bwork.utils.taskexecutor.BWorkManagerTaskExecutor;
import com.ansion.exception.ExceptionExtension;
import com.ansion.utils.thread.ThreadPool;
import com.google.common.util.concurrent.ListenableFuture;

import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Executor;


/**
 * Created by wanghongjie on 2023/5/10
 * Describe : WorkManager 的 实现类
 */
public class BWorkManagerImpl extends BWorkManager {

    private Context mContext;
    private BTaskExecutor mWorkTaskExecutor;
    private WorkDataCache mWorkDataCache;
    private List<BScheduler> mSchedulers;
    private BProcessor mProcessor;
    private static BWorkManagerImpl mInstance = null;
    private static final Object sLock = new Object();

    public static @NonNull
    BWorkManagerImpl getInstance(@NonNull Context context) {
        if (mInstance == null) {
            synchronized (sLock) {
                if (mInstance == null) {
                    mInstance = new BWorkManagerImpl(context,
                            ThreadPool.getIoPool(),
                            new BWorkManagerTaskExecutor(ThreadPool.getIoPool()),
                            BWorkerFactory.getDefaultWorkerFactory(),
                            WorkDataCache.create()
                    );
                }
            }
        }
        return mInstance;
    }

    public BWorkManagerImpl(@NonNull Context context,
                            @NonNull Executor executor,
                            @NonNull BTaskExecutor workTaskExecutor,
                            @NonNull BWorkerFactory workerFactory,
                            @NonNull WorkDataCache dataCache) {
        mContext = context;
        mWorkTaskExecutor = workTaskExecutor;
        mWorkDataCache = dataCache;
        mSchedulers = createSchedulers(context);
        mProcessor = new BProcessor(
                context,
                executor,
                workTaskExecutor,
                workerFactory,
                dataCache,
                mSchedulers);
    }

    public List<BScheduler> createSchedulers(@NonNull Context context) {
        return Arrays.asList(new BGreedyScheduler(context, this));
    }

    @NonNull
    @Override
    public BOperation enqueue(@NonNull List<? extends BWorkRequest> requests) {
        if (requests.isEmpty()) {
            ExceptionExtension.throwThrowable(new IllegalArgumentException("enqueue needs at least one WorkRequest."));
        }
        return new BWorkContinuationImpl(this, requests).enqueue();
    }

    @NonNull
    @Override
    public BWorkContinuation beginWith(@NonNull List<BOneTimeWorkRequest> work) {
        if (work.isEmpty()) {
            ExceptionExtension.throwThrowable(new IllegalArgumentException("beginWith needs at least one OneTimeWorkRequest."));
        }
        return new BWorkContinuationImpl(this, work);
    }

    @NonNull
    @Override
    public ListenableFuture<BWorkInfo> getWorkInfoById(@NonNull UUID id) {
        BStatusRunnable<BWorkInfo> runnable = BStatusRunnable.forUUID(this, id);
        mWorkTaskExecutor.getBackgroundExecutor().execute(runnable);
        return runnable.getFuture();
    }

    @NonNull
    public ListenableFuture<List<BWorkInfo>> getWorkInfosByTag(@NonNull String tag) {
        BStatusRunnable<List<BWorkInfo>> runnable = BStatusRunnable.forTag(this, tag);
        mWorkTaskExecutor.getBackgroundExecutor().execute(runnable);
        return runnable.getFuture();
    }

    /**
     * 批量监听和单个Work监听的移除皆可用此方法
     *
     * @param workListenerId 此Id必须在 BConstant#WorkListenerId 中定义，且不可重复。
     */
    @Override
    public void removeWorkListenerById(long workListenerId) {
        getWorkDataCache().workCallbackDao().deleteWorkCallback(workListenerId);
    }

    @Override
    public void logout() {
        mWorkDataCache.logout();
    }

    public WorkDataCache getWorkDataCache() {
        return mWorkDataCache;
    }

    public @NonNull
    BTaskExecutor getWorkTaskExecutor() {
        return mWorkTaskExecutor;
    }

    public void startWork(@NonNull String workSpecId) {
        mWorkTaskExecutor.executeOnBackgroundThread(new BStartWorkRunnable(this, workSpecId));
    }

    public void stopWork(@NonNull String workSpecId) {
        mWorkTaskExecutor.executeOnBackgroundThread(new BStopWorkRunnable(this, workSpecId));
    }

    public @NonNull
    List<BScheduler> getSchedulers() {
        return mSchedulers;
    }

    public @NonNull
    BProcessor getProcessor() {
        return mProcessor;
    }
}